{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This notebook serves as a reference for all the container types in HoloViews, with an extensive list of small, self-contained examples wherever possible, allowing each container type to be understood and tested independently. The container types generally need to contain [Elements](Elements.ipynb) to be useful, which are described separately.  We first cover the tree-based containers, which are used in many of the examples elsewhere:\n",
    "\n",
    "\n",
    "<dl class=\"dl-horizontal\">\n",
    "  <dt><a href=\"#Layout\"><code>Layout</code></a></dt><dd>Collect components into a tree, displaying them side by side (<code>+</code> operator)</dd>\n",
    "  <dt><a href=\"#Overlay\"><code>Overlay</code></a></dt><dd>Collect components into a tree, displaying them on top of one another (<code>*</code> operator)</dd>\n",
    "</dl>\n",
    "\n",
    "The remaining container types are most useful for exploring \n",
    "<a id='ParameterSpaceIndex'></a> <a href=\"#Parameter Spaces\">parameter spaces</a>\n",
    "\n",
    "\n",
    "<dl class=\"dl-horizontal\">\n",
    "<dt><a href=\"#HoloMap\"><code>HoloMap</code></a></dt><dd>Visualize N-dimensional spaces using sliders or as an animation. </dd>\n",
    "<dt><a href=\"#GridSpace\"><code>GridSpace</code></a></dt><dd>Parameter space in two dimensions laid out in a grid. </dd>\n",
    "  <dt><a href=\"#NdLayout\"><code>NdLayout</code></a></dt><dd>Parameter space of any dimensionality in a layout with titles.</dd>\n",
    "  <dt><a href=\"#NdOverlay\"><code>NdOverlay</code></a></dt><dd>Parameter space of any dimensionality in an overlay with a legend</dd>\n",
    "</dl>\n",
    "\n",
    "There is a separate [Composing Data](Composing_Data.ipynb) tutorial explaining how each of these can be combined and nested, once you are familiar with the individual containers."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Trees  <a id='Trees'></a>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import holoviews as hv\n",
    "hv.notebook_extension()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To display detailed information about each object displayed in this notebook run the following code in a cell:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "%output info=True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(E.g. just change the type of the above cell to Code, when running this notebook interactively, and then execute it). When ``info=True``, the notebook will pop up a detailed list and explanation of the available options for visualizing each container type, after that notebook cell is executed.  So, to find out all the options for any of these container types, just press ``<Shift-Enter>`` on the corresponding cell in the live notebook. See the [Options tutorial](Options.ipynb) tutorial for detailed information about how to set or examine these options for a given component."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ``Layout``  <a id='Layout'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "``Layout`` places nearly any possible components (both ``Element``s and containers) alongside each other, as described in more detail in the [Introductory tutorial](Introduction.ipynb).  The ``.cols()`` method of ``Layout`` can be used to regroup the components into the specified number of columns for display, if desired."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "points = [(0.1*i, np.sin(0.1*i)) for i in range(100)]\n",
    "pair = hv.Curve(points) + hv.ItemTable([('A',1),('B',2)])\n",
    "pair"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ``Layout`` subfigure labels <a id='subfigure-labels'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By default, a ``Layout`` will label the subfigures as in **A** and **B** above.  You can easily configure this behaviour by setting the ``sublabel_format`` option to ``None`` (no sublabels at all), or something like ``\"{numeric}\"``: 2, ``\"{roman}\"``: ii, ``\"{Roman}\"``: II, ``\"{alpha}\"``: b, or ``\"{Alpha}\"``: B, and you can also change the sublabel size and relative position:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts Layout [sublabel_format=\"({numeric}).\"] Curve [sublabel_size=20] ItemTable [sublabel_position=(0.05, 0.8) sublabel_size=10]\n",
    "pair"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also set these options globally if you consistently prefer a different style, or to disable the subfigure labelling altogether, either conveniently using a line magic in the notebook:\n",
    "\n",
    "```python\n",
    "%opts Layout [sublabel_format=None]\n",
    "```\n",
    "\n",
    "(including tab-completion of option names) or more verbosely using Python code:\n",
    "\n",
    "```python\n",
    "from holoviews.core.options import Store, Options\n",
    "Store.options.Layout = Options('plot', sublabel_format=None)\n",
    "\n",
    "from holoviews.plotting import ElementPlot\n",
    "ElementPlot.set_param(sublabel_size=30,sublabel_position=(1.0,-0.1))\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Layout with custom aspects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The aspect ratios of Elements in a Layout can also be adjusted allowing you to quickly compose a complex figure. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts Image.A [aspect=1.5] Image.C [aspect=0.5 sublabel_position=(-0.7, 0.85) xticks=3]\n",
    "np.random.seed(42)\n",
    "(hv.Image(np.random.rand(25, 25), group='A') +\n",
    " hv.Image(np.random.rand(25, 25), group='B') +\n",
    " hv.Image(np.random.rand(25, 25), group='C'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When aspects vary across elements, there is no single valid arrangement suitable for presenting the elements together. For instance, in the above example, the widths are kept constant while the heights are varying due to the differences in the element aspect ratios. An alternative arrangement may be achieved by setting the ``aspect_weight`` plot option from ``0`` (the default value shown above) to ``1.0``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts Layout [aspect_weight=1.0] Image.A [aspect=1.5] Image.C [aspect=0.5 sublabel_position=(-0.7, 0.85) xticks=3]\n",
    "np.random.seed(42)\n",
    "(hv.Image(np.random.rand(25, 25), group='A') +\n",
    " hv.Image(np.random.rand(25, 25), group='B') +\n",
    " hv.Image(np.random.rand(25, 25), group='C'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``aspect_weight`` parameter can take any value between ``0`` and ``1`` which will adjust how any ``Layout`` containing elements with non square-aspects are presented. Note that when there are multiple rows and columns and many elements with different aspect ratios, it is often necessary to explore the effect of changing this parameter to generate a suitable plot."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Using ``Empty`` as a placeholder in ``Layout`` <a id='Empty'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In order to arrange elements within a ``Layout``, it can sometimes be useful to introduce empty gaps. For this the ``Empty`` pseudo-element may be used as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from holoviews import Curve, ItemTable, Empty\n",
    "sine_points = [(0.1*i, np.sin(0.1*i)) for i in range(100)]\n",
    "cosine_points = [(0.1*i, np.cos(0.1*i)) for i in range(100)]\n",
    "(hv.ItemTable([('A',1),('B',2)]) + hv.Curve(sine_points) + hv.Empty() + hv.Curve(cosine_points)).cols(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ``Empty`` pseudo-element contains no data, cannot be customized in any way, and is never associated with a sub-label. The reason ``Empty`` is called a *pseudo* element is that it is only allowed to be used in ``Layout`` and cannot be used as an element in any other type of container."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ``Overlay``  <a id='Overlay'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Overlays are often built using ``*`` as in the [Introductory tutorial](Introduction.ipynb), but they can also be built by hand.  Using ``vector_data`` from the [``VectorField`` ``Element``](Elements.ipynb#VectorField) example, we can overlay the vector field on top of an ``Image`` component (or any other component, though not all combinations will be useful or clear due to occlusion).  The axis bounds will automatically expand to the largest required to show all of the overlaid items."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts VectorField [size_index=2] (color='r') Image (cmap='gray')\n",
    "x,y  = np.mgrid[-10:10,-10:10] * 0.25\n",
    "sine_rings  = np.sin(x**2+y**2)*np.pi+np.pi\n",
    "exp_falloff = 1/np.exp((x**2+y**2)/8)\n",
    "\n",
    "vector_data = np.array([x.flatten()/5.,           # X positions\n",
    "                        y.flatten()/5.,           # Y positions\n",
    "                        sine_rings.flatten(),     # Arrow angles\n",
    "                        exp_falloff.flatten()])   # Arrow sizes\n",
    "\n",
    "hv.Image(sine_rings) * hv.VectorField(vector_data.T)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parameter Spaces  <a id='Parameter Spaces'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "HoloViews also supplies container classes useful for visualizing parameter spaces or phase spaces, i.e., large collections of results for various combinations of parameters.  These containers allow HoloViews to work with arbitrarily high-dimensional data, while having the underlying data held by ``Element``s ensures that all of the data will be visualizable at every level of each data structure.\n",
    "\n",
    "In addition to the container types discussed here, the [``HeatMap``](Elements#HeatMap) ``Element`` is also useful for visualizing small two-dimensional parameter spaces that have a single value for each location in the space. The [``DynamicMap Tutorial``](Dynamic_Map) shows convenient ways to specify parameter spaces that can be visualized using the objects below. See also the separate [Lancet](http://ioam.github.io/lancet) tool, which works well with HoloViews for launching and collating results from separate computational jobs covering large parameter spaces, which HoloViews can then analyze with ease."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Specifying arbitrary parameter spaces"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First let us define some numpy arrays that we will use to define the types of parameter space below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "frequencies =  np.linspace(0.5,2.0,5)\n",
    "phases = np.linspace(0, np.pi*2, 5)\n",
    "x,y = np.mgrid[-50:51, -50:51] * 0.1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### A parameter space of ``Image`` elements"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sine_array(phase, freq):\n",
    "    return np.sin(phase + (freq*x**2+freq*y**2))\n",
    "\n",
    "matrices = {(p, f): hv.Image(sine_array(p, f), label='Sinusoid Ring', group='Amplitude')\n",
    "          for f in [0.5, 1.0,  1.5,  2.0]    # Frequencies\n",
    "          for p in [0, np.pi/2, np.pi, 3*np.pi/2, 2*np.pi]}  # Phases"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To illustrate that ``matrices`` is a Python dictionary indexed by (phase, frequency), here are two of the dictionary elements side by side:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "matrices[0,0.5] + matrices[np.pi,0.5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### A parameter space of ``Curve`` elements"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sine_curve(phase, freq, samples=102):\n",
    "    xvals = [0.1* i for i in range(samples)]\n",
    "    return [(x, np.sin(phase+freq*x)) for x in xvals]\n",
    "\n",
    "curves = {(round(p,2), f): hv.Curve(sine_curve(p,f)) \n",
    "          for f in [1,2,3,4,5]                               # Frequencies\n",
    "          for p in [0, np.pi/2, np.pi, 3*np.pi/2, 2*np.pi]}  # Phases"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we display two of our curves and then overlay them together with ``*``, which chooses new colors for each new curve according to a predefined color cycle that can be selected as a plot option:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "curves[0,1] + curves[3.14, 2] + curves[0,1] * curves[3.14, 2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ``HoloMap``  <a id='HoloMap'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A ``HoloMap`` is a very powerful multi-dimensional data structure that can hold a very large number of similar ``Element`` objects, e.g. those measured for different values in a parameter space, and then allows easy exploration, animation, and slicing of the parameter and value spaces.  Usage of this type is covered extensively in the [Exploring Data](Exploring_Data.ipynb) tutorial.  Here we show how a ``HoloMap`` can be used to explore all of the different ``Image`` objects created for each combination of phase and frequency:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts Image (cmap='gray')\n",
    "hv.HoloMap(matrices, kdims=['phase', 'frequency'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ``GridSpace``  <a id='GridSpace'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Whenever a ``HoloMap`` has more than one item to display, it will be visualized as an animation or using slider widgets as above, displaying one item (e.g. one ``Element``) at any given time.  If you have up to a two-dimensional parameter space, you can see all your data items at once using a ``GridSpace`` to lay out your data with labelled axes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%output size=140\n",
    "ring_space  = hv.GridSpace(matrices, kdims=['phase', 'frequency'])\n",
    "curve_space = hv.GridSpace(curves,   kdims=['phase', 'frequency'])\n",
    "ring_space + curve_space"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Of course, each item in the grid can also be a combination of other plots, such as an ``Overlay`` or an ``NdOverlay`` (below).\n",
    "\n",
    "## ``GridMatrix``  <a id='GridMatrix'></a>\n",
    "\n",
    "Both ``GridSpace`` and ``GridMatrix`` are very useful for multi-variate analysis. While ``GridSpace`` allows faceting the data by one or two dimensions, a ``GridMatrix`` is most useful to plot multiple variables against each other.\n",
    "\n",
    "A simple example is to plot values drawn from various distributions against each other. First we create a ``Table`` containing the values for each distribution then we apply the ``gridmatrix`` operation, which will generate a ``GridMatrix`` instance:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts GridMatrix [fig_size=150 padding=0.05] Scatter [show_grid=False] (alpha=0.1 s=7) Histogram {+axiswise}\n",
    "distributions = [np.random.rayleigh, np.random.normal, np.random.exponential, np.random.logistic]\n",
    "dist_table = hv.Table(tuple(d(2, size=1000) for d in distributions), vdims=[d.__name__ for d in distributions])\n",
    "hv.operation.gridmatrix(dist_table).relabel(group='Distributions')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This also allows comparing multi-variate distributions easily. Here we generate three-dimensional distributions of various types and pass it to the gridmatrix operation either as a HoloMap or as a NdOverlay allowing us to view the distributions individually or overlaid on top of each other."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%opts Layout [hspace=0.2 fig_size=150] Scatter (alpha=0.1) Histogram (alpha=0.3)\n",
    "cycle = hv.plotting.Cycle()\n",
    "holomap = hv.HoloMap({d.__name__: hv.Points(d(2, size=(1000, 3)))\n",
    "                      for i, d in enumerate(distributions)}, kdims=['Distribution'])\n",
    "hv.operation.gridmatrix(holomap) + hv.operation.gridmatrix(holomap.overlay())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ``NdLayout``  <a id='NdLayout'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "``GridSpace`` is great when you have a two-dimensional parameter space, but fails to scale well beyond that. For higher-dimensional parameter spaces, you can use an ``NdLayout``, where the varying key dimensions are shown in the titles of the elements. An ``NdLayout`` is thus more verbose than an ``GridSpace``, and the structure of the parameter space is not as immediately clear (since one has to read the titles to see the parameter values), but it can enumerate all the possible locations in a multidimensional space."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from holoviews import NdLayout\n",
    "hv.NdLayout(matrices, kdims=['phase', 'frequency'])[0:1.6, 0:2].cols(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ``NdOverlay``  <a id='NdOverlay'></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "``NdOverlay`` is to ``Overlay`` what ``NdLayout`` is to ``Layout``.  In other words, an ``NdOverlay`` is a way of looking at a parameter space as an ``Overlay``. However,``NdOverlay`` is generally less useful than ``NdLayout``, because some ``Element`` types don't overlay nicely over each other (e.g. multiple ``Image`` elements just obscure one another). ``NdOverlay`` is a nice, compact representation when it works well, but it is easy for an ``NdOverlay`` to present too much data jumbled together, overwhelming the viewer rather than revealing the data's structure.\n",
    "\n",
    "Unlike a regular ``Overlay``, the elements of an ``NdOverlay`` must always be of the same type.\n",
    "\n",
    "To demonstrate how it works, we will overlay several of the curves from our phase space. To make sure the result is legible, we filter our parameter space down to four curves:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%output size=150\n",
    "four_curves = {(p, f): val for ((p,f), val) in curves.items() if p <=np.pi/2 and f<=2}\n",
    "hv.NdOverlay(four_curves, kdims=['Phase', 'Frequency'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Because ``NdOverlay`` ensures all the contained elements are of the same type, it can now supply a useful legend. If you try changing ``four_curves`` to ``curves`` in the above call to ``NdOverlay``, you can see what happens when trying to visualize too many ``Element``s together in an overlay.\n",
    "\n",
    "As with other aspects of HoloViews, overlaying is a very general concept, and it works with any other type that can be meaningfully overlaid. Here is another example using ``Points``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%output size=150\n",
    "np.random.seed(10)\n",
    "extents = (-3, -3, 3, 3)\n",
    "hv.NdOverlay({1: hv.Points(np.random.normal(size=(50,2)), extents=extents),\n",
    "              2: hv.Points(np.random.normal(size=(50,2)), extents=extents),\n",
    "              3: hv.Points(np.random.normal(size=(50,2)), extents=extents)},\n",
    "             kdims=['Cluster'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I.e., you can easily use overlays to make new types of plots or new organizations, starting from the existing ``Element`` types.\n",
    "\n",
    "The above examples focus on using the container types to do visualization, but they are also useful just for storing and cataloging your data even if the entire set of it can't be visualized at once. That is, the containers let you embed your data in whatever multidimensional numeric or categorical space best characterizes it, as described in the [Exploring Data tutorial](Exploring_Data.ipynb).  Once organized in this way, specific portions of your data can then be flexibly sliced, indexed, or sampled as described in the [Sampling Data tutorial](Sampling_Data.ipynb), allowing you to focus on specific interesting or important areas of this space as you explore it.  See the [Columnar Data tutorial](Columnar_Data.ipynb) for more information about how to organize your data to facilitate analysis and visualization."
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
